/*
* Copyright 2010 Pavel Kozhin 
*
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
* http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed  under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License.
*
* Author: Pavel Kozhin.
*/

package org.etherframework.behaviour.rules
{
	import flash.events.Event;
	import flash.events.IEventDispatcher;
	
	import mx.core.IMXMLObject;
	import mx.core.mx_internal;
	
	import org.etherframework.behaviour.rules.context.Context;
	import org.etherframework.behaviour.rules.context.ContextDistributor;
	import org.etherframework.behaviour.rules.context.ContextEvent;
	import org.etherframework.behaviour.rules.context.IContext;
	import org.etherframework.behaviour.rules.core.Chain;
	import org.etherframework.behaviour.rules.core.IControl;
	import org.etherframework.util.CallLater;
	
	use namespace mx_internal;
	
	[Event(name="complete", type="org.etherframework.behaviour.rules.events.FeedbackEvent")]

	public class Rule extends Chain implements IRule
	{
		private static const COMPLETE:String = "complete";
		
		private var events:Array = [];
		
		private var distributor:ContextDistributor;
		
		private var callingLater:Boolean = false;
		
		public function Rule()
		{
			super();
			distributor = new ContextDistributor();
		}
		
		override public function initialize():void
		{
			if (!(_type || _dispatcher))
				throw new Error("EventHandler: type or dispatcher is undefined.");
			
			super.initialize();
			
			_dispatcher.addEventListener(type, eventHandler);
		}
		
		private var _type:String;
		
		public function get type():String
		{
			return _type;
		}
		
		public function set type(value:String):void
		{
			_type = value;
		}
		
		private var _dispatcher:IEventDispatcher;
		
		public function get dispatcher():IEventDispatcher
		{
			return _dispatcher;
		}
		
		public function set dispatcher(value:IEventDispatcher):void
		{
			if (_dispatcher && type)
				_dispatcher.removeEventListener(type, eventHandler);
			
			_dispatcher = value;
		}
		
		private var _feedbackEnabled:Boolean = false;

		public function get feedbackEnabled():Boolean
		{
			return _feedbackEnabled;
		}

		public function set feedbackEnabled(value:Boolean):void
		{
			_feedbackEnabled = value;
		}
		
		private function executeContext():void
		{
			if (!events.length)
			{
				context = null;
				return;
			}
			
			if (distributor.idle && enabled)
			{
				var event:Event = events.pop() as Event;
				var target:Object = ("initiator" in event) ? event["initiator"] : event.target; 
				var evt:Event = (("data" in event) && (event["data"] is Event)) ? event["data"] : event; 
				var context:IContext = new Context(this, null, target, [], evt.type, evt);
				context.addEventListener(ContextEvent.CLOSE, contextCloseHandler, false, 100);
				
				distributor.start(this, context, sortFunction);
				
				context.open();
			}
		}
		
		private function contextCloseHandler(event:Event):void
		{
			event.target.removeEventListener(ContextEvent.CLOSE, contextCloseHandler);
			dispatchFeedback(COMPLETE);
			
			if (!callingLater)
			{
				CallLater.execute2(callLaterHandler);
				callingLater = true;
			}
		}
		
		private function callLaterHandler():void
		{
			callingLater = false;
			executeContext();
		}
		
		private function eventHandler(event:Event):void
		{
			if(IRules(descriptor.owner).preventPropagation)
			{
				event.preventDefault();
			}
			
			events.unshift(event);
			executeContext();
		}
		
		private function sortFunction(a:IControl, b:IControl):Number 
		{
			var aPriority:Number = a.index - a.descriptor.index;
			var bPriority:Number = b.index - b.descriptor.index;
			var aIndex:Number = a.index;
			var bIndex:Number = b.index;
			
			if(((aIndex == bIndex) ? aPriority > bPriority : aIndex > bIndex)) 
			{
				return 1;
			} 
			else if(((aIndex == bIndex) ? aPriority < bPriority : aIndex < bIndex)) 
			{
				return -1;
			} 
			else  
			{
				return 0;
			}
		}
		
	}
}